package com.erlang4j.internal.basicIo;

import static com.erlang4j.api.Erlang4jMessageLanguage.atom;
import static com.erlang4j.api.Erlang4jMessageLanguage.binding;
import static com.erlang4j.api.Erlang4jMessageLanguage.make;
import static com.erlang4j.api.Erlang4jMessageLanguage.makeArray;
import static com.erlang4j.api.Erlang4jMessageLanguage.tuple;
import static com.erlang4j.internal.utilities.Erlang4jCollectionHelper.join;

import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangPid;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.erlang4j.api.IBinding;
import com.erlang4j.api.IMailBox;
import com.erlang4j.api.exceptions.Erlang4jInvalidReplyException;
import com.erlang4j.internal.messages.IMessageComposer;
import com.erlang4j.internal.messages.IMessageParser;
import com.erlang4j.internal.messages.MessageComposer;
import com.erlang4j.internal.messages.MessageParser;

/**
 * This is a wrapped around a basic mail box.
 * 
 * 
 * <p>
 * Design notes
 * <ul>
 * <li>This is an example of "prefer composition to inheritance": a normal alternative would be an inheritance structure
 * <li>This allows much easier wrapping of mail boxes for purposes of trace, profiling, debugging, as the IBasicMailBox
 * wrappers are easier to write than the mail box wrappers (fewer interfaces, and more stable code)
 * <li>If only we had mixins in Java, then this code would be a lot nicer
 * </ul>
 * </p>
 * 
 * @author Phil Rice
 */

public class MailBox implements IMailBox {

	protected final IBasicMailBox basicOperations;
	protected final IMessageComposer composer;
	protected final IMessageParser parser = new MessageParser();

	public MailBox(IBasicMailBox basicOperations) {
		this.basicOperations = basicOperations;
		this.composer = new MessageComposer(binding("Self", self()));
	}

	@Override
	public void sendRaw(Object serverNameOrPid, Object object) {
		basicOperations.send(serverNameOrPid, make(object));
	}

	@Override
	public void send(Object serverNameOrPid, String pattern, Object... parameters) {
		basicOperations.send(serverNameOrPid, composer.compose(pattern, parameters));
	}

	@Override
	public void sendWithBinding(Object serverNameOrPid, String pattern, IBinding binding) {
		basicOperations.send(serverNameOrPid, composer.composeFromBinding(pattern, binding));
	}

	@Override
	public void sendRpc(Object serverNameOrPid, String atom, Object... parameters) {
		OtpErlangTuple data = tuple((Object[]) makeArray(join(atom(atom), parameters)));
		basicOperations.send(serverNameOrPid, tuple(basicOperations.self(), data));
	}

	public OtpErlangObject rpc(String serverNameOrPid, String name, Object object) {
		sendRpc(serverNameOrPid, name, object);
		OtpErlangObject result = receive();
		IBinding binding = parser.parse(result, "{Pid,Data}", binding());
		if (binding.isValid())
			return binding.getRaw("Data");
		throw new Erlang4jInvalidReplyException(result);
	}

	@Override
	public OtpErlangObject receive() {
		return basicOperations.receive();
	}

	@Override
	public OtpErlangPid self() {
		return basicOperations.self();
	}
}
